home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 July: Mac OS SDK / Dev.CD Jul 99 SDK2.toast / Development Kits / Hardware / Mac OS USB DDK / Examples / USBSampleStorageDriver / StorageClassShim.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-12  |  10.6 KB  |  397 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassShim.c
  3.  
  4.     Contains:    All functions for the Sample USB Storage Class Shim
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     File Ownership:
  11.  
  12.         DRI:                Craig Keithley
  13.  
  14.         Other Contact:        xxx put other contact here xxx
  15.  
  16.         Technology:            USB Drivers
  17.  
  18.     Writers:
  19.  
  20.         (CJK)    Craig Keithley
  21.  
  22.     Change History (most recent first):
  23.  
  24.       <USB2>     1/11/99    CJK        update to use sources from 1.1f3 DDK
  25. */
  26.  
  27. /*
  28. MacOS Headers
  29. */
  30. #include <CodeFragments.h>
  31. #include <Devices.h>
  32. #include <DriverServices.h>
  33. #include <MacTypes.h>
  34. #include <Processes.h>
  35. #include <Resources.h>
  36. #include <USB.h>
  37.  
  38.  
  39. /*
  40. Driver Headers
  41. */
  42. #include "StorageClassShim.h"
  43. #include "SampleStorageVersion.h"
  44. #include "SampleStorageDeviceID.h"
  45.  
  46.  
  47. /*
  48. Definitions, Enumerations and Typedefs used by the Shim
  49. */
  50. // Enumerations for the range of Unittable entries
  51. // in which InstallDriverFromMemory will try to install the driver
  52. enum
  53. {
  54.     kUnitTableEntryStart    = 48,
  55.     kUnitTableEntryEnd        = 127
  56. };
  57.  
  58. // These are the structures and definitions used for the Refnum Association array.
  59. // This array handles associating USBDeviceRefs with UnitTable Driver refnums, so
  60. // Drivers can be removed from the UnitTable when the drive is removed.
  61. enum
  62. {
  63.     kMaxNumberOfDrives = 50
  64. };
  65.  
  66. struct RefnumAssociation
  67. {
  68.     USBDeviceRef     theDevRef;
  69.     DriverRefNum    drvrRefNum;
  70. };
  71.  
  72. typedef struct RefnumAssociation    RefnumAssociation;
  73.  
  74.  
  75. /*
  76. Global variables used by the Shim
  77. */
  78. static RefnumAssociation    theRefAssoc[kMaxNumberOfDrives];
  79. static Boolean                 shimInFile;
  80. static FSSpec                shimFSSpec;
  81. static TimerID                gGiveTimeTimer = nil;
  82. static DriverRefNum            gNewDrvrRef = 0;
  83. static USBDeviceNotificationParameterBlock     gpb;
  84.  
  85.  
  86. /* 
  87. Prototypes for internal support functions
  88. */
  89. static OSStatus     GiveTimeSecondaryInterrupt( void *p1, void *p2);
  90. static OSStatus     ShimOpenDriver(USBDeviceRef theDevRef);
  91. static OSStatus     ShimCloseDriver(USBDeviceRef theDevRef);
  92.  
  93.  
  94. /*
  95. Exported Functions for the Code Fragment Manager
  96. */
  97. // This routine gets called by the Code Fragment Manager when the shim's code fragment is loaded
  98. OSErr CFragInitRoutine(CFragInitBlockPtr initBlkPtr)
  99. {
  100.     shimInFile = false;
  101.     
  102.     if (CFragHasFileLocation(initBlkPtr->fragLocator.where))
  103.     {
  104.         shimInFile = true;
  105.         shimFSSpec = *(initBlkPtr->fragLocator.u.onDisk.fileSpec);            // save the FSSpec, in case we need it later
  106.     }
  107.     return noErr;
  108. }
  109.  
  110.  
  111. /*
  112. Functions Exported for the USB Expert
  113. */
  114. // This routine is exported for use by the USB Expert to initialize the shim when
  115. // the expert loads it
  116. OSStatus USBShim(void)
  117. {
  118.     OSStatus status = noErr;
  119.  
  120.     // Clear out the Association array
  121.     BlockZero((Ptr) theRefAssoc, sizeof(RefnumAssociation) * kMaxNumberOfDrives);
  122.  
  123.     // Setup notification for Vendor Specific Devices
  124.     gpb.usbDeviceNotification     = kNotifyAnyEvent;            // tell me about everything
  125.     gpb.usbClass                 = kUSBAnyClass;
  126.     gpb.usbSubClass             = kUSBAnySubClass;
  127.     gpb.usbProtocol             = kUSBAnyProtocol;
  128.     gpb.usbVendor                 = kDriverVendorID;            // Notify me of my specific Vendor ID
  129.     gpb.usbProduct                 = kDriverProductID;            // Notify me of my specific Product ID
  130.     gpb.result = noErr;
  131.     gpb.callback = (USBDeviceNotificationCallbackProcPtr) &myNotificationCallback;        
  132.     gpb.refcon = nil;
  133.      USBInstallDeviceNotification (&gpb);    
  134.  
  135.     return status;
  136. }
  137.  
  138. // This routine is exported for use by the USB Expert to terminate the shim before it gets removed
  139. void USBShimTermination(void)
  140. {
  141.     // Remove the device notofication
  142.     USBRemoveDeviceNotification(gpb.token); 
  143. }
  144.  
  145. // This function is passed to the USB Expert for device add/remove notification
  146. void myNotificationCallback(USBDeviceNotificationParameterBlock *pb)
  147. {
  148.     switch(pb->usbDeviceNotification)        // why were we notified?
  149.     {
  150.         case kNotifyAddDevice:                // because mass storage device appeared
  151.         case kNotifyAddInterface:            // because mass storage interface appeared
  152.         {    
  153.             ShimOpenDriver(pb->usbDeviceRef); 
  154.         }
  155.         break;
  156.             
  157.         case kNotifyRemoveDevice:            // because a mass storage device or interface disappeared
  158.         case kNotifyRemoveInterface:        // because a mass storage device or interface disappeared
  159.         {
  160.             ShimCloseDriver(pb->usbDeviceRef);
  161.         }
  162.         break;
  163.             
  164.         default:
  165.             break;
  166.     }
  167. }
  168.  
  169. /*
  170. Functions Internal to the Shim
  171. */
  172. // Function that will load and install the driver for a new device.
  173. // As stated in the USB DDK API, the notification always occurs at task time.
  174. OSStatus ShimOpenDriver(USBDeviceRef theDevRef)
  175. {
  176.     OSStatus            theErr = noErr;
  177.     Handle                hDrvrResource;
  178.     short                loopCount;
  179.     THz                    currentZone;
  180.     
  181.     // We have been notified of a new device, check to see if we already 
  182.     // have installed a driver the USB Device Ref.
  183.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  184.     {
  185.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  186.         {
  187.             // Driver has already been installed for this USBDeviceRef,
  188.             // Nothing else needs to be done, report back noErr to the notifier
  189.             return noErr;
  190.         }
  191.     }
  192.  
  193.     // No driver has been loaded for this USBDeviceRef, find the driver's 'ndrv' resource        
  194.     if (shimInFile)
  195.     {
  196.         short    oldResFileID = 0, 
  197.                 myResFileID = 0;
  198.  
  199.         SetResLoad(true);
  200.         oldResFileID = CurResFile();                                    // get the current resource file ID
  201.         myResFileID = FSpOpenResFile(&shimFSSpec, fsRdPerm);
  202.         UseResFile(myResFileID);                                        // point at the shim file
  203.  
  204.         currentZone = GetZone ();
  205.         SetZone ( SystemZone() );
  206.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  207.         DetachResource(hDrvrResource);                                    // Detach the resource so it hangs around in the system heap
  208.         SetZone (currentZone);
  209.  
  210.         UseResFile(oldResFileID);                                        // point at the original file
  211.         CloseResFile(myResFileID);                                        // Make sure the resource file is closed
  212.     }
  213.     else
  214.     {
  215.         hDrvrResource = Get1Resource('ndrv', 128);                        // read in the driver from a ndrv resource
  216.     }
  217.     
  218.     // We have found the driver's 'ndrv' resource, install it into the UnitTable
  219.     if (hDrvrResource)
  220.     {
  221.         long                drvrSize;
  222.         Ptr                    pDrvrInMemory;
  223.         DriverRefNum        drvrRefNum;
  224.         void                *pTheStorageClassDispatchTable;
  225.         CFragSymbolClass    symClass;
  226.         CFragConnectionID    connID;
  227.         short                loopCount;
  228.  
  229.         // Lock the Driver Resource in memory
  230.         HLock(hDrvrResource);
  231.  
  232.         // Get the resource information needed to install the Driver
  233.         pDrvrInMemory = *hDrvrResource;
  234.         drvrSize = GetHandleSize(hDrvrResource);
  235.  
  236.         // Install the driver
  237.         theErr = InstallDriverFromMemory(pDrvrInMemory, drvrSize, nil, (RegEntryIDPtr) nil, kUnitTableEntryStart, kUnitTableEntryEnd, &drvrRefNum);
  238.         if ( theErr != noErr )
  239.         {
  240.             // The driver could not be loaded, the shim will return the error and abort the driver load
  241.             return theErr;
  242.         }
  243.  
  244.         gNewDrvrRef = drvrRefNum;
  245.         
  246.         // Save the Driver refnum to remove the driver when the remove notification is recieved
  247.         for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++ )
  248.         {
  249.             if (( theRefAssoc[loopCount].theDevRef == 0 ) && (theRefAssoc[loopCount].drvrRefNum == 0) )
  250.             {
  251.                 theRefAssoc[loopCount].theDevRef = theDevRef;
  252.                 theRefAssoc[loopCount].drvrRefNum = drvrRefNum;
  253.                 break;
  254.             }
  255.         }
  256.  
  257.         // If the array is already full, this drive will not be added to the array.  This means that the Shim will never
  258.         // remove the driver it the drive is detached.  This will only happen if more than kMaxNumberOfDrives are attached
  259.         // at any one time.
  260.  
  261.         // Get the Storage class dispatch table 
  262.         USBGetDriverConnectionID(&theDevRef, &connID);
  263.         currentZone = GetZone ();
  264.         SetZone ( SystemZone() );
  265.         
  266.         theErr = FindSymbol(connID, "\pTheStorageClassDispatchTable", (Ptr *)&pTheStorageClassDispatchTable, &symClass);
  267.         SetZone (currentZone);
  268.  
  269.         // If no error occured, pass the dispatch table pointer to the Driver
  270.         if (theErr == noErr)            
  271.         {
  272.             // Since we know that the Unit Table driver doesn't make any system calls
  273.             // for this control code, we can make the call synchronously.
  274.             theErr = Control(drvrRefNum, 500, &pTheStorageClassDispatchTable);
  275.         }
  276.         
  277.         if ( theErr != noErr )
  278.         {
  279.             // if an error occurs, we should remove the driver from the unittable
  280.             ShimCloseDriver(theDevRef);
  281.         }
  282.     }
  283.     
  284.     return theErr;
  285. }
  286.  
  287. OSStatus ShimCloseDriver(USBDeviceRef theDevRef)
  288. {
  289.     OSStatus            theErr = noErr;
  290.     short                loopCount;
  291.  
  292.     for( loopCount = 0; loopCount < kMaxNumberOfDrives; loopCount++)
  293.     {
  294.         if(theDevRef == theRefAssoc[loopCount].theDevRef)
  295.         {
  296.             if(theRefAssoc[loopCount].drvrRefNum != 0)
  297.             {
  298.                 VCBPtr vol;
  299.                 
  300.                 vol = (VCBPtr) (GetVCBQHdr())->qHead;
  301.                 
  302.                 while (vol)
  303.                 {
  304.                     // Check to see if this volume belongs to the driver
  305.                     // that is being removed
  306.                     if( vol->vcbDRefNum == theRefAssoc[loopCount].drvrRefNum)
  307.                     {
  308.                         IOParam     offlinePB;
  309.                         OSErr        err;
  310.                         
  311.                         BlockZero(&offlinePB, sizeof(IOParam));
  312.                         offlinePB.ioCompletion = nil;    
  313.                         offlinePB.ioResult = noErr;    
  314.                         offlinePB.ioNamePtr = nil;    
  315.                         offlinePB.ioVRefNum = vol->vcbVRefNum;    
  316.                         
  317.                         err = PBUnmountVol((ParamBlockRec *) &offlinePB);
  318.                         if(err == fBsyErr)
  319.                         {
  320.                             err = PBEject((ParamBlockRec *) &offlinePB);
  321.                             gNewDrvrRef = 0;
  322.                         }
  323.                     }
  324.                     
  325.                     vol = (VCBPtr) vol->qLink;
  326.                 }
  327.                 
  328.                 theErr = RemoveDriver(theRefAssoc[loopCount].drvrRefNum, false);
  329.                 theRefAssoc[loopCount].theDevRef = 0;
  330.                 theRefAssoc[loopCount].drvrRefNum = 0;
  331.                 
  332.                 if(gNewDrvrRef == 0)
  333.                 {
  334.                     OSStatus             status;
  335.                     AbsoluteTime        theWait;
  336.                 
  337.                     theWait = DurationToAbsolute(durationSecond);
  338.                     theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  339.                     status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  340.                 }
  341.             }
  342.             
  343.             return theErr;
  344.         }
  345.     }
  346.     
  347.     return theErr;
  348. }
  349.  
  350. // This is the interrupt timer to provide the USB Manager to get time if a device was
  351. // removed with a volume in use.
  352. // The shortcomings of this implementation are:
  353. //         1. This will only work if a single in use device is removed.  Multiple devices can cause
  354. //            problems where the shim could get about confused whether the device was reattached.
  355. //        2. This will only work if the device that is removed has only one busy volume mounted.
  356. //            More than one volume may cause the shim to get confused about whether the volume was remounted
  357. OSStatus GiveTimeSecondaryInterrupt( void *p1, void *p2)
  358. {
  359. #pragma unused ( p1, p2 )
  360.     Boolean     DrvrVolumeFound = false;
  361.  
  362.     gGiveTimeTimer = nil;
  363.  
  364.     if(gNewDrvrRef != 0)
  365.     {
  366.         VCBPtr vol;
  367.         
  368.         vol = (VCBPtr) (GetVCBQHdr())->qHead;
  369.         
  370.         while (vol)
  371.         {
  372.             // Check to see if this volume belongs to the driver
  373.             // that is being removed
  374.             if( vol->vcbDRefNum == gNewDrvrRef)
  375.             {
  376.                 DrvrVolumeFound = true;
  377.                 break;
  378.             }
  379.             
  380.             vol = (VCBPtr) vol->qLink;
  381.         }
  382.     }
  383.  
  384.     if( DrvrVolumeFound == false )
  385.     {
  386.         OSStatus             status;
  387.         AbsoluteTime        theWait;
  388.  
  389.         SystemTask();
  390.         theWait = DurationToAbsolute(durationSecond);
  391.         theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  392.         status = SetInterruptTimer( &theWait, &GiveTimeSecondaryInterrupt, nil, &gGiveTimeTimer);
  393.     }
  394.                 
  395.     return noErr;
  396. }
  397.